/* File:       downloader.c
* Function:    supply the SMS download function
* Creator:     WKW
* Data:        2010.02.26
* status:      create
* History:
*/

/******************************************************************************
*************head file included area***********************
******************************************************************************/
#include <string.h>

#include "downloader.h"

/******************************************************************************
******************static func realize area*****************
******************************************************************************/

static gboolean  downloader_timer(gpointer data)
{
	Downloader *downloader=data;

	if(downloader->download_progress==100)
	{
		pthread_join(downloader->thread_id,NULL);
		if(downloader->finished_func)
			downloader->finished_func(downloader,downloader->user_data);
		
		return FALSE;
	}
	if(downloader->err_id!=CURLE_OK)
	{
		//need to 
		if(downloader->error_func)
			downloader->error_func(downloader,downloader->user_data);
		else
		{
			downloader_stop(downloader,TRUE);
		}
		return FALSE;
	}

	if(downloader->progress_func)
		downloader->progress_func(downloader,downloader->user_data);
	return TRUE;
}

static int downloader_get_source(void *buffer, size_t size, size_t nmemb, void *data)
{
	int len=size*nmemb;
	Downloader *downloader=data;
	if(downloader->out)
	{
		fwrite(buffer, size, nmemb, downloader->out);
	}
	else
	{
		char *p=downloader->data+downloader->data_seek;
		memmove(p,buffer,len);
		downloader->data_seek+=len;
	}
	return len;
}

static int downloader_progress(void *data,double downtotal, double downcurr, double ultotal,double ulnow)
{
	Downloader *downloader=data;
	downloader->download_progress=(int)(downcurr*100.0/downtotal);
	return 0;
}

static void downloader_run_thread(Downloader *downloader)
{
	CURL *curl;
	CURLcode res;

	
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
	
	curl = curl_easy_init();
	if(!curl)
	{
		fprintf(stderr,"curl init error\n");
		goto err;
	}
	
	if(downloader->out_file)
	{
		downloader->out=fopen(downloader->out_file,"w");
		if(!downloader->out)
		{
			fprintf(stderr,"curl creat out file error [%s]\n",downloader->out_file);
			goto err;
		}
	}

	curl_easy_setopt(curl, CURLOPT_URL,downloader->url);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, downloader_get_source);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, downloader); 
	
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,1);

	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
	curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, downloader_progress);
	curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, downloader);
		
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	res = curl_easy_perform(curl);
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
	
	curl_easy_cleanup(curl);
	if(CURLE_OK != res)
	{
		fprintf(stderr, "curl told us %d \n", res);
		downloader->err_id=res;
		goto err;
	}
	downloader->download_progress=100;

	if(downloader->out)
		fclose(downloader->out);
	return ;	
err:
	if(curl)
		curl_easy_cleanup(curl);
	if(downloader->out)
		fclose(downloader->out);
}


/******************************************************************************
******************API func realize area*****************
******************************************************************************/


void downloader_global_init(int argc ,char **argv)
{
	curl_global_init(CURL_GLOBAL_DEFAULT);
}

void downloader_global_cleanup()
{
	curl_global_cleanup();
}

Downloader * downloader_new(const char *url,const char *out_file,int data_len,
							gpointer user_data)
{
	Downloader *downloader;
	
	if(!url)
		return NULL;
	
	downloader = (Downloader *)g_malloc0(sizeof(Downloader)+1);
	downloader->user_data=user_data;
	downloader->url=strdup(url);
	if(out_file)
	{
		downloader->out_file=strdup(out_file);
	}
	else
	{
		if(data_len<=0)
			data_len=DOWNLOADER_DEFAULT_DATA_LEN;
		downloader->data_len=data_len;
		downloader->data=(char *)g_malloc0(sizeof(data_len)+1);
		if(!downloader->data)
		{
			free(downloader->url);
			free(downloader);
			fprintf(stderr,"downloader new error len = %d\n",data_len);
			return NULL;
		}
	}
	
	return downloader;
}

void downloader_delete(Downloader *downloader)
{
	free(downloader->url);
	free(downloader->data);
	free(downloader);
}

gboolean downloader_set_func(Downloader *downloader,const char *key,
							 gboolean (*func)(Downloader* dwonloader,gpointer data))
{
	if(strcmp(key,"download-progress")==0)
		downloader->progress_func=func;
	else if(strcmp(key,"download-finish")==0)
		downloader->finished_func=func;
	else if(strcmp(key,"download-error")==0)
		downloader->error_func=func;
	else
		fprintf(stderr,"downloader not support [%s] func\n",key);
}

void downloader_run(Downloader *downloader)
{	
	int ret;
	ret=pthread_create(&(downloader->thread_id),NULL,
						 (void *) downloader_run_thread,downloader);
	if(ret!=0){
		downloader->err_id=-999;
		if(downloader->error_func)
			downloader->error_func(downloader,NULL);
	}

	downloader->timer=g_timeout_add(200,downloader_timer,downloader);
}

void  downloader_stop(Downloader *downloader,gboolean delete)
{
	if(downloader->thread_id>0)
	{
		pthread_cancel(downloader->thread_id);
		pthread_join(downloader->thread_id,NULL);
	}
	if(downloader->timer>0)
		g_source_remove(downloader->timer);
	if(delete)
		downloader_delete(downloader);
}

